use core::{marker::PhantomData, slice};

use cmsis_rtos2_rs::{osWaitForever, static_mem::EventFlags};
use proto_bytes::ice::proto::BytesCodec;
use utils::{ufmt, ByteBuffer};

use crate::uart::Uart;

const FLAG_TC: u32 = 0x01;
const FLAG_IDLE: u32 = 0x02;

#[derive(Debug, ufmt::derive::uDebug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<E> {
    Timeout,
    Over,
    Invalid(E),
}

pub struct UartContext<const WRITE_BUF_SIZE: usize, const RECV_BUF_SIZE: usize, U: Uart> {
    write_buf: ByteBuffer<WRITE_BUF_SIZE>,
    recv_buf: ByteBuffer<RECV_BUF_SIZE>,
    event_flag: EventFlags,
    _u: PhantomData<U>,
}

impl<const WRITE_BUF_SIZE: usize, const RECV_BUF_SIZE: usize, U: Uart>
    UartContext<WRITE_BUF_SIZE, RECV_BUF_SIZE, U>
{
    pub const fn uninit() -> Self {
        Self {
            write_buf: ByteBuffer::const_new(),
            recv_buf: ByteBuffer::const_new(),
            event_flag: EventFlags::uninit(),
            _u: PhantomData,
        }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        self.event_flag.init_once(name);
    }

    pub fn write_read_until<T: BytesCodec>(
        &self,
        x: T,
        end: &[u8],
        timeout: u32,
    ) -> Result<T, Error<T::Err>> {
        let ptr = self.write_buf.as_mut_ptr();
        let write_buf = unsafe {
            let len = x.to_bytes(ptr);
            slice::from_raw_parts(ptr, len)
        };

        let recv_buf = unsafe { self.recv_buf.as_mut_slice() };
        U::recv_with_dma(recv_buf);

        U::write_with_dma(write_buf);
        _ = self.event_flag.wait_any_flags(FLAG_TC, osWaitForever);

        let mut index = 0;
        loop {
            let rx_index = U::rx_index();
            if rx_index == index {
                self.event_flag
                    .wait_any_flags(FLAG_IDLE, timeout)
                    .map_err(|_| Error::Timeout)?;
                continue;
            } else if rx_index < index {
                return Err(Error::Over);
            }
            index = rx_index;
            let rx_data = &recv_buf[..rx_index];
            if rx_data.ends_with(end) {
                break;
            }
        }
        let data = &recv_buf[..index];
     //   defmt::println!("recv:{:02x}", data);
        T::from_bytes(data).map_err(Error::Invalid)
    }

    pub fn on_idle(&self) {
        self.event_flag.set_flags(FLAG_IDLE);
    }

    pub fn on_tc(&self) {
        self.event_flag.set_flags(FLAG_TC);
    }
}
